/*
 * All rights Reserved, Designed By cloudland Copyright(C) 2010-2014
 *
 * fileName:  IndexWriteHandlerImpl.java
 * author:  lei
 * Company:  Cloudland Studio
 * Createdate:  2013-9-19 下午9:43:54
 *   
 * Modification  History:
 *     Date           Author        Version        
 * -------------------------------------------
 * 2013-9-19          Lei            1.0        
 *
 * Why & What is modified:
 * 1.0: 文件创建
 */
package org.cloudland.search.handler.support;

import java.beans.IntrospectionException;
import java.beans.PropertyDescriptor;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.text.ParseException;
import java.util.Date;
import java.util.HashMap;

import org.apache.log4j.Logger;
import org.apache.lucene.document.Document;
import org.apache.lucene.document.Field;
import org.apache.lucene.document.FieldType;
import org.apache.lucene.index.IndexWriter;
import org.apache.lucene.index.Term;
import org.cloudland.search.build.FieldMapping;
import org.cloudland.search.config.FieldTypeEnum;
import org.cloudland.search.exception.ConstantsException;
import org.cloudland.search.exception.SearchException;
import org.cloudland.search.handler.IndexWrite;

 /**
 * <pre> 
 * 索引写操作接口实现类。
 * </pre>
 * @ClassName  IndexWriteHandlerImpl
 * @author  lei
 * @version  %I%, %G%
 * @see  
 * @since   JDK1.6
 *
 */
public class IndexWriteHandlerImpl implements IndexWrite {

	/**
     * LOGGER 日志
     */
    private static Logger LOGGER = Logger.getLogger(IndexWriteHandlerImpl.class);
    
    /**
     * writer 索引写入流操作
     */
    private IndexWriter writer;
    
    /**
     * target 索引映射字段对象集合
     */
    private FieldMapping[] target;
    
    /**
     * autoCommit 是否自动提交
     */
    private boolean autoCommit = true;
    
    /**
     * close 是否已关闭
     */
    private boolean close = false;
    
    /**
     * methods 类方法集合
     */
    private HashMap<String, Method> methods;
    
    /**
     * Field 主键字段, 用于更新操作
     */
    private Field primaryField;
    
    /** 
     * default constructor
     */
    public IndexWriteHandlerImpl(IndexWriter writer, FieldMapping[] mapping) {
    	this.writer = writer;
    	this.target = mapping;
    }
    
    /** 
     * 解析获取传入类的读取方法集合
     *
     * @param beanClass
     * @throws IntrospectionException 
     */
    private void resolve(Class<?> beanClass) throws IntrospectionException {
    	
    	if (null == methods || methods.isEmpty()) {
    		methods = new HashMap<String, Method>();
        	
        	PropertyDescriptor desc = null;
        	for (FieldMapping mapping : target) {
        		desc = new PropertyDescriptor(mapping.getTargetName(), beanClass);
        		methods.put(mapping.getTargetName(), desc.getReadMethod());
        		
        		if (mapping.isPrimary()) {
        			primaryField = mapping.getFiled();
        		}
        	}
    	}
    	
    }
    
    /** 
     * 将数据对象转换成所有文档对象处理方法。<br>
     *
     * @param obj
     * @return
     * @throws IllegalArgumentException
     * @throws IllegalAccessException
     * @throws InvocationTargetException
     * @throws ParseException 
     */
    private Document doDocument(Object obj) throws IllegalArgumentException, IllegalAccessException, InvocationTargetException, ParseException {
    	
    	Document doc = new Document();
    	
    	// 所有建立字段记录文本集合
    	StringBuffer detail = new StringBuffer();
    	Method method = null;
    	Object value = null;
    	Field field = null;
    	for (FieldMapping mapping : target) {
            method = methods.get(mapping.getTargetName());
            value = method.invoke(obj, new Object[0]);
            if (null != value)
            {
            	field = mapping.getFiled();
                if (FieldTypeEnum.TEXT.equals(mapping.getType())) 
                {
                    field.setStringValue(String.valueOf(value));
                    detail.append(String.valueOf(null == value ? "" : value));
                } 
                else if (FieldTypeEnum.TIME.equals(mapping.getType())) 
                {
                	if (!(value instanceof Date)) {
                		StringBuffer err = new StringBuffer("此方法[" + method.getName() + "]返回的数据类型不是日期类型，无法按日期类型建立索引。请保存配置文件中配置的映射字段类型与实际对象指定的类型一致。");
                		LOGGER.warn(err);
                	}
                    field.setLongValue(((Date)value).getTime());
                } 
                else if (FieldTypeEnum.DECIMAL.equals(mapping.getType())) 
                {
                    field.setFloatValue(Float.valueOf((String.valueOf(null == value ? 0.0F : value))));
                }
                doc.add(field);
            }
		}
    	
    	// detail 字段使用规则
    	FieldType type = new FieldType();
		type.setIndexed(true);
		type.setStored(false);
		type.setOmitNorms(true);
		type.setTokenized(true);
		type.freeze();
		
    	field = new Field("detail", detail.toString(), type);
		doc.add(field);
		
		if (LOGGER.isDebugEnabled()) {
			LOGGER.debug("Document Info: " + doc);
		}
    	
    	return doc;
    }
    
    /**
     * @param autoCommit
     */
    public void setAutoCommit(boolean autoCommit)
    {
    	this.autoCommit = autoCommit;
    }
    
    /**
     * @return
     */
    public boolean isAutoCommit()
    {
        return autoCommit;
    }
    
    /**
     * @return
     */
    public boolean isClose()
    {
        return close;
    }
    
    /**
     * @param source
     * @throws NoSuchMethodException 
     * @throws SecurityException 
     */
    public void add(Object[] source)
    {
        if(null == source || source.length <= 0)
        {
        	LOGGER.warn("Need to index data source is empty.");
        	return;
        }
        try
        {
        	// 解析类
        	resolve(source[0].getClass());
        	
        	for (Object obj : source) 
        	{
        		// 写入索引
        		writer.addDocument(doDocument(obj));
        	}
        	
        	// 判断自动提交
        	if (autoCommit) {
        		// 提交
            	writer.commit();
        	}
        	
        }
        catch (Exception e)
        {
            throw new SearchException(ConstantsException.SEARCH_HANDLER_0005,e);
        }
    }

    /**
     * @param source
     */
    public void update(Object[] source)
    {
        if(null == source || source.length <= 0)
        {
        	LOGGER.warn("Need to update the index of the data source is empty.");
            return;
        }
        
        try
        {
        	// 解析类
        	resolve(source[0].getClass());
        	if (null == primaryField) {
        		LOGGER.warn("Is not configured for the primary key field, unable to proceed with the update operation.");
        		return;
        	}
        	
        	Document doc = null;
        	for (Object obj : source) 
        	{
        		doc = doDocument(obj);
        		
        		// 写入索引
        		writer.updateDocument(new Term(primaryField.name() ,doc.get(primaryField.name())), doc);
        		
        		if (LOGGER.isDebugEnabled()) {
        			StringBuffer buff = new StringBuffer("Process document update handler. [Term:{name: " + primaryField.name() + ", value: " + doc.get(primaryField.name()) + "}");
        			LOGGER.debug(buff);
        		}
        	}
        	
        	// 判断自动提交
        	if (autoCommit) {
        		// 提交
            	writer.commit();
        	}
        	
        }
        catch (Exception e)
        {
            throw new SearchException(ConstantsException.SEARCH_HANDLER_0006,e);
        }
        
    }
    
    /**
     * 
     */
    public void delete()
    {
        try
        {
            writer.deleteAll();
        }
        catch (IOException e)
        {
        	LOGGER.error("索引写操作关闭异常",e);
            throw new SearchException(ConstantsException.SEARCH_HANDLER_0001,e);
        }
    }
    
    /**
     * 
     */
    public void commit()
    {
        try
        {
            writer.commit();
        }
        catch (IOException e)
        {
        	LOGGER.error("索引写操作提交异常",e);
            throw new SearchException(ConstantsException.SEARCH_HANDLER_0001,e);
        }
    }
    
    /**
     * 
     */
    public void close()
    {
    	if (close) {
    		return;
    	}
    	
        try
        {
            writer.close();
            close = true;
        }
        catch (IOException e)
        {
        	LOGGER.error("索引写操作关闭异常", e);
            throw new SearchException(ConstantsException.SEARCH_HANDLER_0002,e);
        }
    }

}
